package cn.lg.soar.common.util.data;

import cn.lg.soar.common.algorithm.CryptoUtils;
import cn.lg.soar.common.util.AssertUtil;
import lombok.Getter;
import lombok.Setter;

import java.nio.charset.StandardCharsets;
import java.time.Duration;

/**
 * 数据签名
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Getter
@Setter
public class SignatureData {

    /**
     * 截止时间（什么时间后失效）
     */
    private Long deadline;

    /**
     * 随机字符串
     */
    private String randomStr;

    /**
     * 数据（非字符串数据可转为json，接收方再解析json）
     */
    private String data;

    /**
     * 数据签名
     */
    private String signature;

    /**
     * 创建签名数据
     * @param data 数据
     * @param secret 密钥
     * @param expire 有效时长（秒）
     * @return
     */
    public static SignatureData create(String data, String secret, long expire) {
        SignatureData signature = new SignatureData();
        signature.setDeadline(System.currentTimeMillis() + (expire * 1000));
        signature.setRandomStr(RandomUtil.getString(32, 64));
        signature.setData(data);
        signature.setSignature(genSignature(signature, secret));
        return signature;
    }

    /**
     * 创建签名数据
     * @param data 数据
     * @param secret 密钥
     * @param expire 有效时长
     * @return
     */
    public static SignatureData create(String data, String secret, Duration expire) {
        return create(data, secret, expire.getSeconds());
    }

    /**
     * 解析获取数据（验签通过后返回数据，验签失败抛异常）
     * @param data
     * @param secret
     * @return
     */
    public static String parse(SignatureData data, String secret) {
        AssertUtil.isTrue(NumberUtil.gt(data.getDeadline(), System.currentTimeMillis()), "数据已过期");
        AssertUtil.isTrue(genSignature(data, secret).equals(data.getSignature()), "签名无效");
        return data.getData();
    }

    /**
     * 生成签名
     * @param data
     * @param secret
     * @return
     */
    public static String genSignature(SignatureData data, String secret) {
        String string = String.format("%s:%s:%s:%s", data.getDeadline(), data.getRandomStr(), data.getData(), secret);
        return CryptoUtils.sm3Str(string.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 验证签名
     * @param data 数据
     * @param secret 密钥
     * @return
     */
    public static boolean verify(SignatureData data, String secret) {
        // 先验证时间在验证签名
        return NumberUtil.gt(data.getDeadline(), System.currentTimeMillis()) &&
                genSignature(data, secret).equals(data.getSignature());
    }

}
